home *** CD-ROM | disk | FTP | other *** search
/ Mac Power 1997 December / MACPOWER-1997-12.ISO.7z / MACPOWER-1997-12.ISO / AMUG / PROGRAMMING / Raven 1.2.sit / Raven 1.2 / Source / Foundation / Common / ZStringUtils.cpp < prev    next >
Text File  |  1997-08-07  |  13KB  |  574 lines

  1. /*
  2.  *  File:       ZStringUtils.cpp
  3.  *  Summary:       Misc string utilities
  4.  *  Written by: Jesse Jones
  5.  *
  6.  *  Copyright ゥ 1996-1997 Jesse Jones. 
  7.  *    For conditions of distribution and use, see copyright notice in ZTypes.h  
  8.  *
  9.  *  Change History (most recent first):    
  10.  *
  11.  *         <3>     8/05/97    JDJ        Added LoadAppString and LoadRavenString.
  12.  *         <2>     7/27/97    JDJ        Added StripTrailing.
  13.  *         <1>     8/09/96    JDJ        Created (from ZMiscUtils)
  14.  */
  15.  
  16. #include <ZStringUtils.h>
  17.  
  18. #include <CType.h>
  19. #include <Map.h>
  20. #include <Resources.h>
  21. #include <String.h>
  22. #include <TextUtils.h>
  23.  
  24. #include <ZConstants.h>
  25. #include <ZDebug.h>
  26. #include <ZExceptions.h>
  27. #include <ZUnitTest.h>
  28.  
  29.  
  30. //-----------------------------------
  31. //    Types
  32. //
  33. #pragma options align=mac68k
  34.  
  35. struct SXEntry {
  36.     short    code;
  37.     Str255    str;
  38. };
  39.  
  40. struct SXTable {                    // 'STRX' resource format
  41.     short    numEntries;
  42.     SXEntry    entries[1];
  43. };
  44.  
  45. typedef map<string, string, less<string>, allocator<string> >    ZStringMap;
  46.  
  47. #pragma options align=reset
  48.  
  49.  
  50. //---------------------------------------------------------------
  51. //
  52. // NextEntry
  53. //
  54. // We have to do this grody pointer arithmetic because ResEdit only 
  55. // saves the characters that are actually used by the string.
  56. //
  57. //---------------------------------------------------------------
  58. static SXEntry* NextEntry(SXEntry* entry)
  59. {
  60.     Byte* ptr = (Byte *) entry;
  61.     
  62.     ptr += entry->str[0] + 3;
  63.     if ((long) ptr & 1)                            // ResEdit will add a pad byte for odd strings.
  64.         ptr++;
  65.     
  66.     return (SXEntry *) ptr;
  67. }
  68.  
  69.  
  70. //---------------------------------------------------------------
  71. //
  72. // LoadStringMap
  73. //
  74. //---------------------------------------------------------------
  75. static void LoadStringMap(ResID id, ZStringMap& mapping)
  76. {
  77.     Handle table = GetResource('StrM', id);    
  78.     if (table != nil) {
  79.         HLockHi(table);
  80.         
  81.         short count = *((short*) (*table));
  82.         
  83.         const unsigned char* nextStr = ((const unsigned char*) (*table)) + sizeof(short);
  84.         
  85.         while (count--) {
  86.             const unsigned char* inStr = nextStr;
  87.             nextStr += nextStr[0] + 1;
  88.  
  89.             const unsigned char* outStr = nextStr;
  90.             nextStr += nextStr[0] + 1;
  91.             
  92.             ZStringMap::value_type value(PStrToStr(inStr), PStrToStr(outStr));
  93.             mapping.insert(value);
  94.         }
  95.         
  96.         ReleaseResource(table);
  97.     }
  98. }
  99.  
  100. #pragma mark -
  101.  
  102. // ===================================================================================
  103. //    Conversions
  104. // ===================================================================================
  105.  
  106. //---------------------------------------------------------------
  107. //
  108. // PStrToStr
  109. //
  110. //---------------------------------------------------------------
  111. string PStrToStr(const unsigned char* pStr)
  112. {
  113.     ASSERT(pStr != nil);
  114.  
  115.     return string((char *) pStr+1, *pStr);
  116. }
  117.  
  118.  
  119. //---------------------------------------------------------------
  120. //
  121. // StrToPStr
  122. //
  123. //---------------------------------------------------------------
  124. unsigned char* StrToPStr(const string& cStr)
  125. {
  126.     ASSERT(cStr.length() < 256);
  127.     
  128.     const short kTempStrings = 8;
  129.  
  130.     static short currentString = 0;
  131.     static Str255 strings[kTempStrings];
  132.  
  133.     currentString = (short) ((currentString + 1) % kTempStrings);
  134.  
  135.     ulong length = cStr.length() < 256 ? cStr.length() : 255;
  136.     BlockMoveData(cStr.c_str(), &strings[currentString][1], length);
  137.     strings[currentString][0] = (Byte) length;
  138.  
  139.     return strings[currentString];
  140. }
  141.  
  142.  
  143. //---------------------------------------------------------------
  144. //
  145. // IDToStr
  146. //
  147. //---------------------------------------------------------------
  148. const char* IDToStr(IDType id)
  149. {
  150.     ASSERT(sizeof(ulong) == 4);
  151.     
  152.     const short kTempStrings = 4;
  153.  
  154.     static short currentString = 0;
  155.     static char strings[kTempStrings][5];
  156.  
  157.     currentString = (short) ((currentString + 1) % kTempStrings);
  158.  
  159.     BlockMoveData(&id, &strings[currentString][0], 4UL);
  160.     strings[currentString][4] = '¥0';
  161.  
  162.     return strings[currentString];
  163. }
  164.  
  165.  
  166. //---------------------------------------------------------------
  167. //
  168. // StrToID
  169. //
  170. //---------------------------------------------------------------
  171. IDType StrToID(const string& str)
  172. {
  173.     ASSERT(str.length() == 4);
  174.     
  175.     IDType type;
  176.     
  177.     BlockMoveData(str.c_str(), &type, 4UL);
  178.     
  179.     return type;
  180. }
  181.  
  182. #pragma mark -
  183.  
  184. // ===================================================================================
  185. //    Loading
  186. // ===================================================================================
  187.  
  188. //---------------------------------------------------------------
  189. //
  190. // LoadString
  191. //
  192. //---------------------------------------------------------------
  193. string LoadString(ResID id)
  194. {
  195.     string result;
  196.     
  197.     Handle strH = GetResource('STR ', id);
  198.     ThrowIfResFail(strH);
  199.     
  200.     HLock(strH);
  201.     try {
  202.         result = PStrToStr((unsigned char*) *strH);
  203.     } catch (...) {
  204.         HUnlock(strH);
  205.         throw;
  206.     }
  207.     
  208.     ReleaseResource(strH);
  209.     
  210.     return result;
  211. }
  212.  
  213.  
  214. //---------------------------------------------------------------
  215. //
  216. // LoadIndString
  217. //
  218. //---------------------------------------------------------------
  219. string LoadIndString(ResID strList, int index)
  220. {
  221.     ASSERT(index >= 1);
  222.     
  223.     Str255 str;
  224.     GetIndString(str, strList, (short) index);
  225.     
  226.     return PStrToStr(str);
  227. }
  228.  
  229.  
  230. //---------------------------------------------------------------
  231. //
  232. // LoadAppString
  233. //
  234. //---------------------------------------------------------------
  235. string LoadAppString(const string& inStr)
  236. {
  237.     static ZStringMap mapping;
  238.     static bool inited = false;
  239.     
  240.     if (!inited) {
  241.         LoadStringMap(256, mapping);
  242.         inited = true;
  243.     }
  244.     
  245.     string outStr = inStr;
  246.     
  247.     ZStringMap::iterator iter = mapping.find(inStr);
  248.     if (iter != mapping.end())
  249.         outStr = (*iter).second;
  250.         
  251.     return outStr;
  252. }
  253.  
  254.  
  255. //---------------------------------------------------------------
  256. //
  257. // LoadRavenString
  258. //
  259. //---------------------------------------------------------------
  260. string LoadRavenString(const string& inStr)
  261. {
  262.     static ZStringMap mapping;
  263.     static bool inited = false;
  264.     
  265.     if (!inited) {
  266.         LoadStringMap(128, mapping);
  267.         inited = true;
  268.     }
  269.     
  270.     string outStr = inStr;
  271.     
  272.     ZStringMap::iterator iter = mapping.find(inStr);
  273.     if (iter != mapping.end())
  274.         outStr = (*iter).second;
  275.         
  276.     return outStr;
  277. }
  278.  
  279.  
  280. //---------------------------------------------------------------
  281. //
  282. // LookUpString
  283. //
  284. //---------------------------------------------------------------
  285. string LookUpString(ResID id, int code)
  286. {
  287.     string str = "";
  288.     
  289.     SXTable** table = (SXTable **) GetResource('STRX', id);    
  290.     ThrowIfResFail(table);
  291.     
  292.     HNoPurge((Handle) table);
  293.     ThrowIfMemError();
  294.     
  295.     short count = (**table).numEntries;
  296.     SXEntry* entry = &((**table).entries[0]);
  297.     bool found = false;
  298.     while (count > 0 && !found) {
  299.         found = entry->code == code;
  300.  
  301.         if (!found) {
  302.             entry = NextEntry(entry);
  303.             count--;
  304.         }
  305.     }
  306.     
  307.     if (found) {
  308.         str.resize(entry->str[0] + 1UL);        // resize the string to the correct length to prevent the string ctor from moving memory
  309.         
  310.         str = PStrToStr(entry->str);    
  311.     }
  312.     
  313.     HPurge((Handle) table);
  314.  
  315.     return str;
  316. }
  317.  
  318. #pragma mark -
  319.  
  320. // ===================================================================================
  321. //    Disassembly
  322. // ===================================================================================
  323.  
  324. //---------------------------------------------------------------
  325. //
  326. // Before
  327. //
  328. //---------------------------------------------------------------
  329. string Before(const string& str, const string& sub)
  330. {
  331.     string result;
  332.     
  333.     size_t index = str.find(sub);
  334.     if (index != string::npos)
  335.         result = str.substr(0, index);
  336.     
  337.     return result;
  338. }
  339.  
  340.  
  341. //---------------------------------------------------------------
  342. //
  343. // After
  344. //
  345. //---------------------------------------------------------------
  346. string After(const string& str, const string& sub)
  347. {
  348.     string result;
  349.     
  350.     size_t index = str.find(sub);
  351.     if (index != string::npos)
  352.         result = str.substr(index + sub.length());
  353.     
  354.     return result;
  355. }
  356.  
  357.  
  358. //---------------------------------------------------------------
  359. //
  360. // Parse
  361. //
  362. //---------------------------------------------------------------
  363. string Parse(string& str, const string& term)
  364. {
  365.     string token;
  366.         
  367.     // Start at the first character not in term.
  368.     size_t start = str.find_first_not_of(term);
  369.         
  370.     // Stop at the next occurance of a character in term.
  371.     size_t stop = str.find_first_of(term, start + 1);
  372.         
  373.     // If the start is valid return the token.
  374.     if (start != string::npos)                                         
  375.         token = str.substr(start, stop-start);
  376.         
  377.     // Remove the token from the string.
  378.     if (stop != string::npos)                        
  379.         str = str.substr(stop);                        
  380.     else                    
  381.         str.resize(0);    
  382.         
  383.     // Remove term chars from the start of the string. (Makes it easier
  384.     // to determine when parsing is complete).
  385.     str = Strip(str, term);
  386.                                         
  387.     return token;
  388. }
  389.  
  390. #pragma mark -
  391.  
  392. // ===================================================================================
  393. //    Misc
  394. // ===================================================================================
  395.  
  396. //---------------------------------------------------------------
  397. //
  398. // StrToUpperStr
  399. //
  400. //---------------------------------------------------------------
  401. string StrToUpperStr(const string& lowerStr)
  402. {
  403.     string upperStr = lowerStr;
  404.  
  405.     for (short i = 0; i < (short) upperStr.length(); i++)
  406.         upperStr[i] = (char) toupper(upperStr[i]);
  407.  
  408.     return upperStr;
  409. }
  410.  
  411.  
  412. //---------------------------------------------------------------
  413. //
  414. // StrToLowerStr
  415. //
  416. //---------------------------------------------------------------
  417. string StrToLowerStr(const string& upperStr)
  418. {
  419.     string lowerStr = upperStr;
  420.  
  421.     for (short i = 0; i < (short) lowerStr.length(); i++)
  422.         lowerStr[i] = (char) tolower(lowerStr[i]);
  423.  
  424.     return lowerStr;
  425. }
  426.  
  427.  
  428. //---------------------------------------------------------------
  429. //
  430. // Strip
  431. //
  432. //---------------------------------------------------------------
  433. string Strip(const string& str, const string& padding)
  434. {
  435.     size_t index = 0;
  436.     while (index < str.length() && padding.find(str[index]) != string::npos)
  437.         index++;
  438.         
  439.     return str.substr(index);
  440. }
  441.  
  442.  
  443. //---------------------------------------------------------------
  444. //
  445. // StripTrailing
  446. //
  447. //---------------------------------------------------------------
  448. string StripTrailing(const string& str, const string& padding)
  449. {
  450.     size_t index = 0;
  451.  
  452.     if (!str.empty()) {
  453.         index = str.size();
  454.         while (index > 1 && padding.find(str[index-1]) != string::npos)
  455.             index--;
  456.     }
  457.             
  458.     return str.substr(0, index);
  459. }
  460.  
  461.  
  462. //---------------------------------------------------------------
  463. //
  464. // Replace
  465. //
  466. //---------------------------------------------------------------
  467. string Replace(const string& inStr, char oldCh, char newCh)
  468. {
  469.     string str = inStr;
  470.  
  471.     for (size_t i = 0; i < str.length(); i++)
  472.         if (str[i] == oldCh)
  473.             str[i] = newCh;
  474.     
  475.     return str;
  476. }
  477.  
  478. #pragma mark -
  479.  
  480. // ===================================================================================
  481. //    Unit Test
  482. // ===================================================================================
  483.  
  484. //---------------------------------------------------------------
  485. //
  486. // EqualPStr
  487. //
  488. //---------------------------------------------------------------
  489. #if DEBUG
  490. static bool EqualPStr(unsigned char* s1, unsigned char* s2)
  491. {
  492.     ASSERT(s1 != nil);
  493.     ASSERT(s2 != nil);
  494.     
  495.     return memcmp(s1, s2, *s1 + 1UL) == 0;
  496. }
  497. #endif    // DEBUG
  498.  
  499.  
  500. //---------------------------------------------------------------
  501. //
  502. // TestStringUtils
  503. //
  504. //---------------------------------------------------------------
  505. #if DEBUG
  506. static void TestStringUtils()
  507. {
  508.     ASSERT(PStrToStr("¥p") == "");                    // PStrToStr
  509.     ASSERT(PStrToStr("¥ph") == "h");            
  510.     ASSERT(PStrToStr("¥pHello") == "Hello");            
  511.     
  512.     ASSERT(EqualPStr(StrToPStr(""), "¥p"));            // StrToPStr
  513.     ASSERT(EqualPStr(StrToPStr("h"), "¥ph"));            
  514.     ASSERT(EqualPStr(StrToPStr("Hello"), "¥pHello"));            
  515.     
  516.     ASSERT(strcmp(IDToStr('TEXT'), "TEXT") == 0);    // IDToStr
  517.     ASSERT(strcmp(IDToStr('????'), "????") == 0);            
  518.     ASSERT(strcmp(IDToStr('jpg '), "jpg ") == 0);            
  519.     
  520.     ASSERT(StrToID("TEXT") == 'TEXT');                // StrToID
  521.     ASSERT(StrToID("????") == '????');            
  522.     ASSERT(StrToID("jpg ") == 'jpg ');            
  523.     
  524.     ASSERT(LoadString(131) == "Save File As:");        // LoadString
  525.     ASSERT(LoadIndString(132, 1) == "Undo ");        // LoadIndString
  526.     ASSERT(LookUpString(203, -39) == "end of file");// LookUpString
  527.     
  528.     string str = "semper fi mac";
  529.     
  530.     ASSERT(Before(str, " fi") == "semper");            // Before
  531.     ASSERT(Before(str, " qq") == "");    
  532.     ASSERT(Before(str, "semp") == "");    
  533.     ASSERT(Before(str, "mac") == "semper fi ");    
  534.     ASSERT(Before(str, "Mac") == "");    
  535.  
  536.     ASSERT(After(str, " fi") == " mac");            // After
  537.     ASSERT(After(str, " qq") == "");    
  538.     ASSERT(After(str, "semp") == "er fi mac");    
  539.     ASSERT(After(str, "mac") == "");    
  540.     ASSERT(After(str, "Mac") == "");    
  541.  
  542.     ASSERT(Parse(str, " f") == "semper");            // Parse
  543.     ASSERT(str == "i mac");
  544.     ASSERT(Parse(str, " f") == "i");
  545.     ASSERT(str == "mac");
  546.     ASSERT(Parse(str, " f") == "mac");
  547.     ASSERT(str == "");
  548.     
  549.     str = "e = M*c^2";                            
  550.     ASSERT(StrToUpperStr(str) == "E = M*C^2");        // StrToUpperStr
  551.     ASSERT(StrToLowerStr(str) == "e = m*c^2");        // StrToLowerStr
  552.     
  553.     str = "   oolong";                                // Strip
  554.     ASSERT(Strip(str, " ") == "oolong");
  555.     ASSERT(Strip(str, " o") == "long");
  556.     ASSERT(Strip(str, " l") == "oolong");
  557.     ASSERT(Strip(str, "o ") == "long");
  558.     ASSERT(Strip(str, "o") == "   oolong");
  559.     
  560.     ASSERT(Replace("hello", 'l', 'k') == "hekko");    // Replace
  561.     ASSERT(Replace("hekko", 'h', 'k') == "kekko");
  562.     ASSERT(Replace("kekko", 'o', 'k') == "kekkk");
  563.     ASSERT(Replace("kekkk", 'e', 'k') == "kkkkk");
  564.     ASSERT(Replace("kkkkk", 'k', 'j') == "jjjjj");
  565.     ASSERT(Replace("h", 'h', 'k') == "k");
  566.     ASSERT(Replace("", 'h', 'k') == "");
  567.     
  568.     TRACE("Completed str utils test.¥n¥n");
  569. }
  570.  
  571. static TUnitTestRegistrar sStringReg("String Utils", TestStringUtils);
  572.  
  573. #endif    // DEBUG
  574.